package com.chengyu.stock.service.impl;

import com.chengyu.stock.constant.ParseType;
import com.chengyu.stock.mapper.StockBusinessMapper;
import com.chengyu.stock.mapper.StockMarketIndexInfoMapper;
import com.chengyu.stock.mapper.StockRtInfoMapper;
import com.chengyu.stock.pojo.entity.StockMarketIndexInfo;
import com.chengyu.stock.pojo.entity.StockRtInfo;
import com.chengyu.stock.pojo.vo.StockInfoConfig;
import com.chengyu.stock.service.StockTimerTaskService;
import com.chengyu.stock.utils.DateTimeUtil;
import com.chengyu.stock.utils.IdWorker;
import com.chengyu.stock.utils.ParserStockInfoUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.Now;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service("stockTimerTaskService")
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    /**
     * 维护股票数据采集的http请求实体对象，避免重复构建
     */
    private HttpEntity<Object> httpEntity;

    /**
     * 定义bean的初始化方法，当前bean对象被构建时，会回调该方法
     */
    @PostConstruct
    public void initMethod(){
        //2.组装请求对象
        HttpHeaders httpHeaders = new HttpHeaders();
        //设置防盗链：说白就是告诉对象服务器，我访问的url地址是跳转子站内的可信的地址
        httpHeaders.add("Referer","https://finance.sina.com.cn/stock/");
        //认证认证：以浏览器方式
        httpHeaders.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        //构建http实体对象
        this.httpEntity= new HttpEntity<>(httpHeaders);
    }

    //采集国内大盘数据方法
    @Override
    public void collectInnerMarketInfo() {
        //定义采集的url接口
        String url = stockInfoConfig.getMarketUrl() + String.join(",", stockInfoConfig.getInner());
        //调用restTemplate采集数据


        //使用resetTemplate 发起请求
        String restString = restTemplate.postForObject(url, httpEntity, String.class);
        log.info("当前采集的数据：{}", restString);

        //数据解析
        String reg = "var hq_str_(.+)=\"(.+)\";";
        //编译表达式 获取编译对象
        Pattern pattern = Pattern.compile(reg);
        //匹配字符串
        Matcher matcher = pattern.matcher(restString);
        ArrayList<StockMarketIndexInfo> list = new ArrayList<>();
        //判断是否有匹配的值
        while (matcher.find()) {
            //获取正则组
            //大盘编码
            String marketCode = matcher.group(1);
            //其它
            String otherInfo = matcher.group(2);
            String[] infos = otherInfo.split(",");

            //大盘名称
            String marketName = infos[0];
            //开盘点
            BigDecimal openPoint = new BigDecimal(infos[1]);
            //前收盘点
            BigDecimal preClosePoint = new BigDecimal(infos[2]);
            //当前点
            BigDecimal curPoint = new BigDecimal(infos[3]);
            //最高点
            BigDecimal mxPoint = new BigDecimal(infos[4]);
            //最低点
            BigDecimal minPoint = new BigDecimal(infos[5]);
            //成交量
            Long tradeAmt = Long.valueOf(infos[8]);
            //成交金额
            BigDecimal tradeVol = new BigDecimal(infos[9]);
            //获取当前时间
            Date curDate = DateTimeUtil.getDateTimeWithoutSecond(infos[30] + " " + infos[31]).toDate();
            //构建大盘实体对象
            //将获取到的数据存入entity 中的 StockMarketIndexInfo 对象中
            StockMarketIndexInfo info = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId())
                    .marketCode(marketCode)
                    .marketName(marketName)
                    .curPoint(curPoint)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .maxPoint(mxPoint)
                    .minPoint(minPoint)
                    .tradeVolume(tradeVol)
                    .tradeAmount(tradeAmt)
                    .curTime(curDate)
                    .build();
            list.add(info);
        }
        //判断有没有取到数据
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        //批量插入
        int count = stockMarketIndexInfoMapper.insertBatch(list);
        if (count > 0) {
            log.info("时间：{}，保存个股数据成功！内容为：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), list);
            //通知stock_backend刷新缓存
            //给个时间 让backend判断是否超过一分钟
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
        } else {
            log.error("时间：{}，保存个股数据失败！内容为：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), list);
        }
    }

    /**
     * 采集个股数据
     */
    @Override
    public void collectStockInfos() {
        //获取所有个股编码集合
        List<String> allCodes = stockBusinessMapper.getStockIds();
        //添加sh sz业务前缀，以6开头则表示上海证券交易所，其它就是深证
        allCodes = allCodes.stream().map(code -> code.startsWith("6") ? "sh" + code : "sz" + code).collect(Collectors.toList());
        System.out.println(allCodes);
        //显然如果直接追加会导致url地址过长，所以需要将这个集合分片处理：40：分片，每片最多15只股票  15 15 10
//       String join = String.join(",", allCodes);
        //利用guava的集合工具类将大的集合进行分片处理
        Lists.partition(allCodes, 15).forEach(codes -> {
            //原始方式采集
            //组装url
//            String stockURL = stockInfoConfig.getMarketUrl() + String.join(",", codes);
//            //调用restTemplate采集数据
//            //组装请求头
//            HttpHeaders headers = new HttpHeaders();
//            headers.add("Referer", "https://finance.sina.com.cn/stock/");
//            headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
//            //组装请求对象
//            HttpEntity<Object> entity = new HttpEntity<>(headers);
//            //使用resetTemplate 发起请求
//            ResponseEntity<String> respEntity = restTemplate.exchange(stockURL, HttpMethod.GET, entity, String.class);
//            int codeValue = respEntity.getStatusCodeValue();
//            if (codeValue != 200) {
//                log.error("当前时间：{},采集个股数据失败，状态码：{},响应的数据：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), codeValue, respEntity.getBody());
//                return;
//            }
//            String jsData = respEntity.getBody();
//            //解析原始的js格式数据封装成个股实体对象
//            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
//            System.out.println("list = " + list);
//            int count = stockRtInfoMapper.insertBatch(list);
//            if (count > 0) {
//                log.info("时间：{}，保存个股数据成功！内容为：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), list);
//            } else {
//                log.error("时间：{}，保存个股数据失败！内容为：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), list);
//            }
            //方案2 多线程
            //方案3：基于线程池采集股票数据，好处：1.避免线程资源频繁创建和销毁，提高资源复用性 2.设置可使用线程数的上限，避免影响其它业务线程
            threadPoolTaskExecutor.submit(()->{
                //组装url
                String stockURL = stockInfoConfig.getMarketUrl() + String.join(",", codes);
                //调用restTemplate采集数据

                //使用resetTemplate 发起请求
                ResponseEntity<String> respEntity = restTemplate.exchange(stockURL, HttpMethod.GET, httpEntity, String.class);
                int codeValue = respEntity.getStatusCodeValue();
                if (codeValue != 200) {
                    log.error("当前时间：{},采集个股数据失败，状态码：{},响应的数据：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), codeValue, respEntity.getBody());
                    return;
                }
                String jsData = respEntity.getBody();
                //解析原始的js格式数据封装成个股实体对象
                List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
                System.out.println("list = " + list);
                int count = stockRtInfoMapper.insertBatch(list);
                if (count > 0) {
                    log.info("时间：{}，保存个股数据成功！内容为：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), list);
                } else {
                    log.error("时间：{}，保存个股数据失败！内容为：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"), list);
                }
            });
        });
    }
}
